﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using DonutLandLibrary.Data;

namespace DonutLandLibrary.Robots.Environments
{
    public class RobotEnvironment
    {
        private const int rowSize = 40;
        private const int columnSize = 40;
        private const int maxPowerSources = 3500;
        private const int maxCorrosivePatches = 350;
        private const int maxPowerSourcesPerSquare = 5;

        private int cycleCount;

        private List<Robot> robotCollection;

        private SquareCollection squareCollection;

        public int CycleCount
        {
            get
            {
                return cycleCount;
            }
            set
            {
                cycleCount = value;
            }
        }

        public List<Robot> RobotCollection
        {
            get
            {
                return robotCollection;
            }
        }

        public int ID { get; set; }

        public int RowSize
        {
            get
            {
                return rowSize;
            }
        }

        public int ColumnSize
        {
            get
            {
                return columnSize;
            }
        }

        public int MaxPowerSources
        {
            get { return maxPowerSources; }
        }

        public int MaxCorrosivePatches
        {
            get { return maxCorrosivePatches; }
        }

        public int MaxPowerSourcesPerSquare
        {
            get { return maxPowerSourcesPerSquare; }
        }

        public SquareCollection Squares
        {
            get
            {
                return squareCollection;
            }
            set
            {
                squareCollection = value;
            }
        }

        public RobotEnvironment()
        {
            squareCollection = new SquareCollection(rowSize, columnSize);
            robotCollection = new List<Robot>();

            cycleCount = 0;

            squareCollection.randomlyPopulateSquares(maxPowerSources, maxCorrosivePatches, maxPowerSourcesPerSquare);
        }

        public void Save()
        {
            if (ID == 0)
            {
                ID = EnvironmentsData.SaveEnvironment(this);
            }
            else
            {
                EnvironmentsData.UpdateEnvironment(this);
            }
        }

        public static RobotEnvironment Load(int environmentID)
        {
            return EnvironmentsData.GetEnvironment(environmentID);
        }

        public static int CheckRowCoordinatesForWrap(int coordinate)
        {
            if (coordinate < 0)
            {
                coordinate = rowSize + coordinate;
            }

            if (coordinate > rowSize - 1)
            {
                coordinate = 0;
            }

            return coordinate;
        }

        public static int CheckColumnCoordinatesForWrap(int coordinate)
        {
            if (coordinate < 0)
            {
                coordinate = columnSize + coordinate;
            }

            if (coordinate > columnSize - 1)
            {
                coordinate = 0;
            }

            return coordinate;
        }

        public static List<int> GetEnvironments()
        {
            return EnvironmentsData.GetEnvironments();
        }

        public void SpawnRobot()
        {
            Robots.GeneticAlgorithm.GeneticProfile geneProfile;
            Robot spawnedRobot;
            RandomGenerator randomGenerator = RandomGenerator.getInstance();

            //Randomize the genetic profile
            int maxEnergyLevel = randomGenerator.RandomInt(0, 8);
            int maxProtectiveCasing = randomGenerator.RandomInt(0, 8);
            int eneDepletionRate = randomGenerator.RandomInt(1, 8);
            int maxEnergyDepletionReserve = randomGenerator.RandomInt(0, 50);
            int proCasingRepairRate = randomGenerator.RandomInt(1, 8);
            int proCasingRepairReserve = randomGenerator.RandomInt(0, 50);

            geneProfile = new Robots.GeneticAlgorithm.GeneticProfile(maxEnergyLevel, maxProtectiveCasing, eneDepletionRate, maxEnergyDepletionReserve, proCasingRepairRate, proCasingRepairReserve);

            geneProfile.ID = GeneticProfilesData.Save(geneProfile);

            spawnedRobot = new Robot(geneProfile, cycleCount, cycleCount);

            spawnedRobot.CurrentSquare =  Squares.retrieveSquareAtCoordinate(randomGenerator.RandomInt(0, 39), randomGenerator.RandomInt(0, 39));
            spawnedRobot.CurrentEnvironment = this;

            spawnedRobot.CurrentSquare.RobotsOnSquare.Add(spawnedRobot);

            if (robotCollection == null)
            {
                robotCollection = new List<Robot>();
            }

            spawnedRobot.ID = RobotsData.Save(spawnedRobot);

            robotCollection.Add(spawnedRobot);
        }

        public void Cycle()
        {
            int deathCount = 0;
            int count;
            string childGeneProfile;
            Robot childRobot;
            RandomGenerator randomGenerator = RandomGenerator.getInstance();
            Square newSquare;

            cycleCount++;
            
            foreach (Robot robot in robotCollection)
            {
               squareCollection = robot.UpdateRobot(squareCollection, cycleCount);
            }

            count = 0;

            while (count < robotCollection.Count){
                //update the robot in the DB with the death.
                RobotsData.Update(robotCollection[count]);

                //remove robot if its dead
                if (!robotCollection[count].RobotAlive)
                {
                    robotCollection.RemoveAt(count);
                    deathCount++;
                }
                else
                {
                    count++;
                }
            }

            count = 0;

            while (count < deathCount)
            {
                DonutLandLibrary.Robots.GeneticAlgorithm.GeneticProfile childGeneticProfile;

                //childGeneProfile = GetFittestRobot().GeneticProfile.Reproduce(GetSecondFittestRobot().GeneticProfile);

                childGeneProfile = Robots.GeneticAlgorithm.GeneticProfile.SelectRobotByRoulette(robotCollection).GeneticProfile.Reproduce(Robots.GeneticAlgorithm.GeneticProfile.SelectRobotByRoulette(robotCollection).GeneticProfile);

                childGeneticProfile = new GeneticAlgorithm.GeneticProfile(childGeneProfile);

                childGeneticProfile.ID = GeneticProfilesData.Save(childGeneticProfile);

                childRobot = new Robot(childGeneticProfile,
                                        cycleCount,
                                        cycleCount);

                newSquare = Squares.retrieveSquareAtCoordinate(randomGenerator.RandomInt(0, 39), randomGenerator.RandomInt(0, 39));

                //while (newSquare.RobotOnSquare != null)
                //{
                //    newSquare = Squares.retrieveSquareAtCoordinate(randomGenerator.RandomInt(0, 39), randomGenerator.RandomInt(0, 39));
                //}

                childRobot.CurrentSquare = newSquare;
                childRobot.CurrentEnvironment = this;

                childRobot.CurrentSquare.RobotsOnSquare.Add(childRobot);

                if (robotCollection == null)
                {
                    robotCollection = new List<Robot>();
                }

                childRobot.ID = RobotsData.Save(childRobot);

                robotCollection.Add(childRobot);

                EnvironmentSquaresData.UpdateRobotEnvironmentSquare(childRobot.CurrentSquare, ID);

                count++;
            }
        }

        //private Robot GetFittestRobot()
        //{
        //    List<Robot> robotFitnessList;

        //    robotFitnessList = robotCollection.OrderBy(x => x.Fitness).ToList();

        //    if (robotFitnessList.Count > 0)
        //    {
        //        return robotFitnessList[0];
        //    }
        //    else
        //    {
        //        if (PreviousFittestRobot != null)
        //        {
        //            return PreviousFittestRobot;
        //        }
        //        else
        //        {
        //            throw new Exception("No robots to reproduce from!");
        //        }
        //    }
        //}

        //private Robot GetSecondFittestRobot()
        //{
        //    List<Robot> robotFitnessList;

        //    robotFitnessList = robotCollection.OrderBy(x => x.Fitness).ToList();

        //    if (robotFitnessList.Count > 1)
        //    {
        //        return robotFitnessList[1];
        //    }
        //    else
        //    {
        //        if (PreviousSecondFittestRobot != null)
        //        {
        //            return PreviousSecondFittestRobot;
        //        }
        //        else
        //        {
        //            return GetFittestRobot();
        //        }
        //    }
        //}
    }
}
